#import "WKPingTools.h"
#include <arpa/inet.h>
#include <netdb.h>

// 错误域定义
NSString *const kHDPingErrorDomain = @"com.hd.ping.error";

#pragma mark - HDPingTimeInterval 实现（时间间隔模型）
@implementation HDPingTimeInterval

+ (instancetype)intervalWithSecond:(NSTimeInterval)second {
    HDPingTimeInterval *interval = [[self alloc] init];
    if (interval) {
        interval->_type = HDPingTimeIntervalTypeSecond;
        interval->_value = second;
        interval->_secondValue = second; // 秒无需转换
    }
    return interval;
}

+ (instancetype)intervalWithMillisecond:(NSTimeInterval)millisecond {
    HDPingTimeInterval *interval = [[self alloc] init];
    if (interval) {
        interval->_type = HDPingTimeIntervalTypeMillisecond;
        interval->_value = millisecond;
        interval->_secondValue = millisecond / 1000.0; // 毫秒转秒
    }
    return interval;
}

+ (instancetype)intervalWithMicrosecond:(NSTimeInterval)microsecond {
    HDPingTimeInterval *interval = [[self alloc] init];
    if (interval) {
        interval->_type = HDPingTimeIntervalTypeMicrosecond;
        interval->_value = microsecond;
        interval->_secondValue = microsecond / 1000000.0; // 微秒转秒
    }
    return interval;
}

@end

#pragma mark - HDPingResponse 实现（Ping 响应模型）
@implementation HDPingResponse
@end

#pragma mark - HDPingItem 实现（内部 Ping 任务模型）
@implementation HDPingItem
@end

#pragma mark - HDPingNetworkActivityIndicator 实现（状态栏指示器）
@implementation HDPingNetworkActivityIndicator

+ (instancetype)sharedIndicator {
    static HDPingNetworkActivityIndicator *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
        instance.isHidden = YES; // 默认隐藏
    });
    return instance;
}

- (void)setIsHidden:(BOOL)isHidden {
    _isHidden = isHidden;
    // 主线程更新 UI，避免线程安全问题
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIApplication sharedApplication].networkActivityIndicatorVisible = !isHidden;
    });
}

- (void)updateWithTime:(NSInteger)time {
    self.isHidden = NO;
    // 显示后延迟 0.5 秒隐藏，模拟网络活动状态
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.isHidden = YES;
    });
    // 调试日志：输出响应时间
    WKPingTools *tempTool = [[WKPingTools alloc] init];
    if (tempTool.debugLog) {
        NSLog(@"HDPingIndicator: 目标响应时间 = %ldms", (long)time);
    }
}

@end

#pragma mark - HDPingTools 核心实现
@implementation WKPingTools {
    SimplePing *_pinger;          // 底层 Ping 实例（依赖 SimplePing）
    HDPingTimeInterval *_pingInterval; // 重复 Ping 间隔
    HDPingCompleteBlock _complete;     // 回调 Block
    HDPingItem *_lastSendItem;         // 最后一次发送的任务
    HDPingItem *_lastReciveItem;       // 最后一次接收的任务
    NSTimer *_sendTimer;               // 重复发送定时器
    NSTimer *_checkTimer;              // 超时检测定时器
    NSString *_pingAddressIP;          // 解析后的目标 IP
}

#pragma mark - 构造方法
- (instancetype)initWithHostName:(NSString * _Nullable)hostName {
    self = [super init];
    if (self) {
        // 处理默认主机名（为空时默认 www.apple.com）
        NSString *targetHost = hostName ?: @"www.apple.com";
        if (targetHost.length == 0) {
            targetHost = @"www.apple.com";
        }
        // 初始化 SimplePing 并设置代理
        _pinger = [[SimplePing alloc] initWithHostName:targetHost];
        _pinger.delegate = self;
        
        // 默认配置参数
        _timeout = [HDPingTimeInterval intervalWithMillisecond:1000]; // 1秒超时
        _debugLog = YES;          // 默认开启调试日志
        _stopWhenError = NO;      // 错误时不默认停止
        _showNetworkActivityIndicator = HDNetworkActivityIndicatorStatusAuto; // 自动显示状态栏指示器
        _isPing = NO;             // 初始状态：未在 Ping
        _isRunning = NO;          // 初始状态：未运行
    }
    return self;
}

- (instancetype)initWithURL:(NSURL * _Nullable)url {
    // 从 URL 中提取主机名（如 https://www.baidu.com → www.baidu.com）
    return [self initWithHostName:url.host];
}

#pragma mark - 公开方法：简化版 start（默认参数）
- (void)startWithComplete:(HDPingCompleteBlock _Nullable)complete {
    // 默认：自动适配 IPv4/IPv6，无重复 Ping（间隔为 0）
    [self startWithPingType:SimplePingAddressStyleAny
                    interval:[HDPingTimeInterval intervalWithSecond:0]
                    complete:complete];
}

#pragma mark - 公开方法：带参数 start（自定义 IP 类型和间隔）
- (void)startWithPingType:(SimplePingAddressStyle)pingType
                  interval:(HDPingTimeInterval * _Nullable)interval
                  complete:(HDPingCompleteBlock _Nullable)complete {
    // 移除旧通知监听，避免重复注册
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    // 注册后台/前台切换通知：后台停止 Ping，前台恢复
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(_didEnterBackground)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(_didBecomeActive)
                                                 name:UIApplicationDidBecomeActiveNotification
                                               object:nil];
    
    // 启动核心 Ping 逻辑
    [self _startCoreWithPingType:pingType interval:interval complete:complete];
}

#pragma mark - 公开方法：停止 Ping
- (void)stop {
    // 移除所有通知监听
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    // 更新状态栏指示器状态
    HDPingNetworkActivityIndicator *indicator = [HDPingNetworkActivityIndicator sharedIndicator];
    if (_showNetworkActivityIndicator == HDNetworkActivityIndicatorStatusAuto ||
        _showNetworkActivityIndicator == HDNetworkActivityIndicatorStatusNone) {
        indicator.isHidden = YES;
    } else {
        indicator.isHidden = NO;
    }
    
    // 停止核心逻辑
    [self _stopCore];
}

#pragma mark - 私有方法：核心启动逻辑
- (void)_startCoreWithPingType:(SimplePingAddressStyle)pingType
                      interval:(HDPingTimeInterval * _Nullable)interval
                      complete:(HDPingCompleteBlock _Nullable)complete {
    // 先停止旧任务，避免重复运行
    [self _stopCore];
    
    // 初始化参数
    _pingInterval = interval ?: [HDPingTimeInterval intervalWithSecond:0];
    _complete = [complete copy]; // Block 复制到堆区，避免栈区释放导致野指针
    _pinger.addressStyle = pingType;
    _isRunning = YES;
    
    // 更新状态栏指示器
    HDPingNetworkActivityIndicator *indicator = [HDPingNetworkActivityIndicator sharedIndicator];
    if (_showNetworkActivityIndicator == HDNetworkActivityIndicatorStatusAuto ||
        _showNetworkActivityIndicator == HDNetworkActivityIndicatorStatusAlways) {
        indicator.isHidden = NO;
    } else {
        indicator.isHidden = YES;
    }
    
    // 启动 SimplePing
    [_pinger start];
    
    // 配置重复 Ping（间隔 > 0 时启用定时器）
    if (_pingInterval.secondValue > 0) {
        _sendTimer = [NSTimer scheduledTimerWithTimeInterval:10
                                                     repeats:YES
                                                       block:^(NSTimer * _Nonnull timer) {
                                                           // 定时重复调用核心启动逻辑
                                                           [self _startCoreWithPingType:pingType
                                                                                  interval:interval
                                                                                  complete:complete];
                                                       }];
        // 将定时器添加到主运行循环（确保后台模式下也能触发）
        [[NSRunLoop mainRunLoop] addTimer:_sendTimer forMode:NSRunLoopCommonModes];
    }
}

#pragma mark - 私有方法：核心停止逻辑
- (void)_stopCore {
    // 清理单次 Ping 的资源（如定时器、状态标记）
    [self _cleanSinglePingResource];
    
    // 停止重复发送定时器
    [_sendTimer invalidate];
    _sendTimer = nil;
    
    // 更新运行状态
    _isRunning = NO;
}

#pragma mark - 私有方法：清理单次 Ping 资源（成功/失败均调用）
- (void)_cleanSinglePingResource {
    // 停止 SimplePing
    [_pinger stop];
    
    // 重置单次 Ping 相关状态
    _isPing = NO;
    _lastSendItem = nil;
    _lastReciveItem = nil;
    _pingAddressIP = nil;
    
    // 停止超时检测定时器
    [_checkTimer invalidate];
    _checkTimer = nil;
}

#pragma mark - 通知回调：应用进入后台
- (void)_didEnterBackground {
    if (_debugLog) {
        NSLog(@"HDPingTools: 应用进入后台，停止 Ping 任务");
    }
    [self _stopCore];
}

#pragma mark - 通知回调：应用回到前台
- (void)_didBecomeActive {
    if (_debugLog && _isRunning) {
        NSLog(@"HDPingTools: 应用回到前台，恢复 Ping 任务");
    }
    // 若之前处于运行状态，恢复 Ping
    if (_isRunning) {
        [self startWithPingType:_pinger.addressStyle
                        interval:_pingInterval
                        complete:_complete];
    }
}

#pragma mark - 私有方法：发送 Ping 数据
- (void)_sendPingData {
    // 避免重复发送（当前正在 Ping 时跳过）
    if (_isPing) {
        return;
    }
    // 发送空数据（SimplePing 会自动填充 ICMP 协议头部）
    [_pinger sendPingWithData:nil];
}

#pragma mark - 私有方法：格式化 IP 地址（NSData → 字符串）
- (NSString *)_formatIPAddressFromData:(NSData *)addressData {
    char hostStr[NI_MAXHOST] = {0}; // 存储格式化后的 IP 字符串
    const struct sockaddr *sockAddr = [addressData bytes]; // 转换为 sockaddr 结构体
    
    // 调用系统 API 解析 IP 地址（仅返回数字格式，不反向解析域名）
    int result = getnameinfo(sockAddr,
                             (socklen_t)addressData.length,
                             hostStr,
                             sizeof(hostStr),
                             NULL,
                             0,
                             NI_NUMERICHOST);
    
    return (result == 0) ? [NSString stringWithUTF8String:hostStr] : @"未知 IP 地址";
}

#pragma mark - 私有方法：简化错误信息（便于日志输出）
- (NSString *)_getSimpleErrorDescFromError:(NSError *)error {
    // 处理 CFNetwork 域名解析错误（如 DNS 失败）
    if ([error.domain isEqualToString:(NSString *)kCFErrorDomainCFNetwork] &&
        error.code == kCFHostErrorUnknown) {
        NSNumber *failureCode = error.userInfo[(NSString *)kCFGetAddrInfoFailureKey];
        if (failureCode.intValue != 0) {
            const char *errorMsg = gai_strerror((int)failureCode.intValue);
            if (errorMsg) {
                return [NSString stringWithUTF8String:errorMsg];
            }
        }
    }
    // 优先返回具体错误原因，无则返回错误描述
    return error.localizedFailureReason ?: error.localizedDescription;
}

#pragma mark - 私有方法：触发回调（确保主线程）
- (void)_triggerCompleteWithResponse:(HDPingResponse * _Nullable)response
                               error:(NSError * _Nullable)error {
    // 切换到主线程回调，避免 UI 操作在子线程导致崩溃
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self->_complete) {
            self->_complete(response, error);
        }
    });
}

#pragma mark - 私有方法：创建自定义 Ping 错误
- (NSError *)_createPingErrorWithCode:(HDPingErrorCode)code
                              message:(NSString *)message {
    NSDictionary *errorInfo = @{NSLocalizedDescriptionKey: message,
                                NSLocalizedFailureReasonErrorKey: message};
    return [NSError errorWithDomain:kHDPingErrorDomain
                               code:code
                           userInfo:errorInfo];
}

#pragma mark - SimplePingDelegate 代理方法（Ping 生命周期回调）
// Ping 启动成功（获取到目标地址）
- (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address {
    _pingAddressIP = [self _formatIPAddressFromData:address];
    if (_debugLog) {
        NSLog(@"HDPingTools: Ping 启动成功，目标 IP = %@", _pingAddressIP);
    }
    // 启动后立即发送一次 Ping 数据
    [self _sendPingData];
}

// Ping 启动失败
- (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error {
    NSString *errorDesc = [self _getSimpleErrorDescFromError:error];
    if (_debugLog) {
        NSLog(@"HDPingTools: Ping 启动失败，原因 = %@", errorDesc);
    }
    // 更新状态栏指示器
    [[HDPingNetworkActivityIndicator sharedIndicator] updateWithTime:0];
    // 触发失败回调
    NSError *pingError = [self _createPingErrorWithCode:HDPingErrorRequest
                                                message:[NSString stringWithFormat:@"Ping 启动失败：%@", errorDesc]];
    [self _triggerCompleteWithResponse:nil error:pingError];
    // 清理资源
    [self _cleanSinglePingResource];
    // 若配置错误时停止，终止整个 Ping 任务
    if (_stopWhenError) {
        [self _stopCore];
    }
}

// Ping 数据包发送成功
- (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet sequenceNumber:(UInt16)sequenceNumber {
    if (_debugLog) {
        NSLog(@"HDPingTools: Ping 数据包发送成功，大小 = %lu 字节，序列号 = %u",
              (unsigned long)packet.length, sequenceNumber);
    }
    // 标记为正在 Ping
    _isPing = YES;
    // 记录发送时间和序列号
    _lastSendItem = [[HDPingItem alloc] init];
    _lastSendItem.sendTime = [NSDate date];
    _lastSendItem.sequence = sequenceNumber;
    // 启动 SimplePing
//    [_pinger start];
}

// Ping 数据包发送失败
- (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet sequenceNumber:(UInt16)sequenceNumber error:(NSError *)error {
    NSString *errorDesc = [self _getSimpleErrorDescFromError:error];
    if (_debugLog) {
        NSLog(@"HDPingTools: Ping 数据包发送失败，序列号 = %u，原因 = %@",
              sequenceNumber, errorDesc);
    }
    // 更新状态栏指示器
    [[HDPingNetworkActivityIndicator sharedIndicator] updateWithTime:0];
    // 触发失败回调
    NSError *pingError = [self _createPingErrorWithCode:HDPingErrorReceive
                                                message:[NSString stringWithFormat:@"数据包发送失败：%@", errorDesc]];
    [self _triggerCompleteWithResponse:nil error:pingError];
    // 清理资源
    [self _cleanSinglePingResource];
    // 若配置错误时停止，终止整个 Ping 任务
    if (_stopWhenError) {
        [self _stopCore];
    }
}
// 收到 Ping 响应数据包（成功）
- (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet sequenceNumber:(UInt16)sequenceNumber {
    // 校验发送记录（避免接收无关响应）
    if (!_lastSendItem || _lastSendItem.sequence != sequenceNumber) {
        if (_debugLog) {
            NSLog(@"HDPingTools: 收到无效响应（序列号不匹配），忽略");
        }
        return;
    }
    // 修复后
    if (!_lastSendItem || !_lastSendItem.sendTime) {
        // 处理空值情况（如日志输出或默认值）
        if (_debugLog) {
            NSLog(@"HDPingTools: 无法计算响应时间，发送记录为空");
        }
        return; // 或设置默认值
    }

    NSTimeInterval responseTimeMs = [[NSDate date] timeIntervalSinceDate:_lastSendItem.sendTime] * 1000;
    responseTimeMs = round(responseTimeMs * 10) / 10; // 保留一位小数
    
    if (_debugLog) {
        NSLog(@"HDPingTools: 收到响应，IP = %@，大小 = %lu 字节，序列号 = %u，响应时间 = %.1fms",
              _pingAddressIP, (unsigned long)packet.length, sequenceNumber, responseTimeMs);
    }
    // 更新状态栏指示器（显示响应时间）
    [[HDPingNetworkActivityIndicator sharedIndicator] updateWithTime:(NSInteger)responseTimeMs];
    
    // 构造响应模型
    HDPingResponse *response = [[HDPingResponse alloc] init];
    response.pingAddressIP = _pingAddressIP;
    response.responseBytes = (NSInteger)packet.length;
    response.responseTime = [HDPingTimeInterval intervalWithMillisecond:responseTimeMs];
    
    // 记录接收状态
    _lastReciveItem = [[HDPingItem alloc] init];
    _lastReciveItem.sendTime = [NSDate date];
    _lastReciveItem.sequence = sequenceNumber;
    
    // 触发成功回调
    [self _triggerCompleteWithResponse:response error:nil];
    // 清理单次 Ping 资源
    [self _cleanSinglePingResource];
}

// 收到未知数据包（非预期响应）
- (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet {
    if (_debugLog) {
        NSLog(@"HDPingTools: 收到未知数据包，大小 = %lu 字节，忽略", (unsigned long)packet.length);
    }
    NSError *pingError = [self _createPingErrorWithCode:HDPingErrorReceive
                                                message:[NSString stringWithFormat:@"收到未知数据包"]];
    [self _triggerCompleteWithResponse:nil error:pingError];
    // 无需触发错误回调（仅忽略），但清理单次 Ping 资源
    [self _cleanSinglePingResource];
    // 若配置错误时停止，终止整个 Ping 任务
    if (_stopWhenError) {
        [self _stopCore];
    }
}

@end
